package com.mashuai.utils.toolkit.core.msConverts;

import com.mashuai.utils.toolkit.achieve.MsExegesis;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author mashuai
 * @ClassName: TimesUtil
 * @Description: 时间工具类
 */
@MsExegesis("时间工具类")
public class MsTimeUtils {
    private static final String TIME_TYPE = "方法所有时间类型入参：year = 年，month = 月，day = 天，hour = 小时， minute = 分钟 ，second = 秒";
    public static ThreadLocal<DateFormat> sdfYmdHms = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    public static ThreadLocal<DateFormat> sdfYmdHm = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm"));
    public static ThreadLocal<DateFormat> sdfYmdH = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH"));
    public static ThreadLocal<DateFormat> sdfYmd = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
    public static ThreadLocal<DateFormat> sdfYm = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM"));
    public static ThreadLocal<DateFormat> sdfMd = ThreadLocal.withInitial(() -> new SimpleDateFormat("MM-dd"));
    public static ThreadLocal<DateFormat> sdfY = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy"));
    public static ThreadLocal<DateFormat> sdfM = ThreadLocal.withInitial(() -> new SimpleDateFormat("MM"));
    public static ThreadLocal<DateFormat> sdfD = ThreadLocal.withInitial(() -> new SimpleDateFormat("dd"));
    public static ThreadLocal<DateFormat> sdfHms = ThreadLocal.withInitial(() -> new SimpleDateFormat("HH:mm:ss"));
    public static ThreadLocal<DateFormat> sdfHm = ThreadLocal.withInitial(() -> new SimpleDateFormat("HH:mm"));
    public static ThreadLocal<DateFormat> sdfH = ThreadLocal.withInitial(() -> new SimpleDateFormat("HH"));
    public static ThreadLocal<DateFormat> sdfYmdHmsJoin = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMddHHmmss"));

    /**
     * 获取当前时间
     *
     * @param f 任意格式，决定了返回值
     * @return 返回字符串时间
     */
    @MsExegesis("获取当前时间")
    public static String getTimeNowStr(ThreadLocal<DateFormat> f) {
        return f.get().format(new Date());
    }

    /**
     * 获取当前时间，返回任意格式的Date类型
     */
    @MsExegesis("获取当前时间，返回任意格式的Date类型")
    public static Date getTimeNowDate(ThreadLocal<DateFormat> f) {
        try {
            return f.get().parse(f.get().format(new Date()));
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 把String类型时间，格式化任意需要的时间格式
     */
    @MsExegesis("把String类型时间，格式化任意需要的时间格式")
    public static String strTimeFormat(ThreadLocal<DateFormat> f, String time) {
        return dateConvertStr(f, strConvertDate(f, time));
    }

    /**
     * 把date类型时间，转换成任意格式的字符串时间
     */
    @MsExegesis("把date类型时间，转换成任意格式的字符串时间")
    public static String dateConvertStr(ThreadLocal<DateFormat> f, Date date) {
        return f.get().format(date);
    }

    /**
     * 把String类型时间，转换成任意格式的Date时间
     */
    @MsExegesis("把String类型时间，转换成任意格式的Date时间")
    public static Date strConvertDate(ThreadLocal<DateFormat> f, String date) {
        try {
            return f.get().parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 把时间戳转换成String类型时间
     */
    @MsExegesis("把时间戳转换成String类型时间")
    public static String millsConvertStr(ThreadLocal<DateFormat> f, Long mills) {
        return f.get().format(new Date(mills));
    }

    /**
     * 检查某个小时是否在指定小时范围内
     *
     * @param timeRange 08:00-12:00
     * @param hour      小时。
     * @return true-在范围内
     * @author mashuai
     */
    @MsExegesis("检查某个小时是否在指定小时范围内。timeRange 08:00-12:00；hour 小时。")
    public static boolean checkHourTimeRange(String timeRange, Integer hour) {
        String[] parts = timeRange.split("-");
        String startTimeStr = parts[0];
        String endTimeStr = parts[1];
        LocalTime startTime = LocalTime.parse(startTimeStr);
        LocalTime endTime = LocalTime.parse(endTimeStr);
        LocalTime hourToCheck = LocalTime.of(hour, 0); // 07:00
        if (endTime.isBefore(startTime)) {
            return hourToCheck.isAfter(startTime) || hourToCheck.isBefore(endTime);
        } else {
            return !hourToCheck.isBefore(startTime) && !hourToCheck.isAfter(endTime);
        }
    }

    /**
     * 字符串日期转换成时间戳
     */
    @MsExegesis("字符串日期转换成时间戳")
    public static Long strConvertMills(ThreadLocal<DateFormat> f, String date) {
        Date parse = null;
        try {
            parse = f.get().parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return parse.getTime();
    }

    /**
     * 获取当前指定时间的n前后，n取决于opt类型
     *
     * @param n    负数往前推，正数往后推
     * @param f    时间格式，决定返回的时间格式
     * @param time 以该时间为基准去前后推
     * @param opt  year = 年，month = 月，day = 天，minute = 分钟
     * @author mashuai
     */
    @MsExegesis("获取当前指定时间的n前后，n取决于opt类型。n@负数往前推，正数往后推；f@时间格式，决定返回的时间格式；time@以该时间为基准去前后推；opt@year = 年，month = 月，day = 天，minute = 分钟")
    public static String getBeforeOrAfterTimeDay(ThreadLocal<DateFormat> f, int n, String time, String opt) {
        Calendar c = Calendar.getInstance();
        Date date = null;
        try {
            date = f.get().parse(time);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        c.setTime(date);
        if ("year".equals(opt)) {
            int day1 = c.get(Calendar.YEAR);
            c.set(Calendar.YEAR, day1 + n);
        } else if ("minute".equals(opt)) {
            int day1 = c.get(Calendar.MINUTE);
            c.set(Calendar.MINUTE, day1 + n);
        } else if ("month".equals(opt)) {
            int day1 = c.get(Calendar.MONTH);
            c.set(Calendar.MONTH, day1 + n);
        } else {
            //默认天
            int day1 = c.get(Calendar.DATE);
            c.set(Calendar.DATE, day1 + n);
        }
        String dayResult = f.get().format(c.getTime());
        return dayResult;
    }

    /**
     * 获取一个月的总天数
     **/
    @MsExegesis("获取一个月的总天数")
    public static int getMonthCountDay(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    /**
     * 获取指定时间范围内的所有月份
     * 以2022-01-01@2022-02-01为列
     * 返回的是[2022-01,2022-02]
     */
    @MsExegesis("获取指定时间范围内的所有月份。以2022-01-01@2022-02-01为例，返回的是[2022-01,2022-02]")
    public static List<String> getMonthInterval(ThreadLocal<DateFormat> f, String minDate, String maxDate) throws Exception {
        ArrayList<String> result = new ArrayList<String>();
        Calendar min = Calendar.getInstance();
        Calendar max = Calendar.getInstance();
        DateFormat sdf = f.get();
        min.setTime(sdf.parse(minDate));
        min.set(min.get(Calendar.YEAR), min.get(Calendar.MONTH), 1);
        max.setTime(sdf.parse(maxDate));
        max.set(max.get(Calendar.YEAR), max.get(Calendar.MONTH), 2);
        Calendar curr = min;
        while (curr.before(max)) {
            result.add(sdf.format(curr.getTime()));
            curr.add(Calendar.MONTH, 1);
        }
        return result;
    }

    /**
     * 获取时间端内的所有时间，具体返回值内容取决于传入的格式
     *
     * @param startDate
     * @param endDate
     * @param f
     * @return
     * @author mashuai
     */
    @MsExegesis("获取时间端内的所有时间，具体返回值内容取决于传入的格式")
    public static List<String> getAllTimeInterval(ThreadLocal<DateFormat> f, String startDate, String endDate) {
        DateFormat sdf = f.get();
        int calendarType;
        switch (startDate.length()) {
            case 19:
                calendarType = Calendar.HOUR;
                break;
            case 10:
                calendarType = Calendar.DATE;
                break;
            case 7:
                calendarType = Calendar.MONTH;
                break;
            case 4:
                calendarType = Calendar.YEAR;
                break;
            default:
                return null;
        }
        List<String> result = new ArrayList<>();
        Calendar min = Calendar.getInstance();
        Calendar max = Calendar.getInstance();
        try {
            min.setTime(sdf.parse(startDate));
            min.add(calendarType, 0);
            max.setTime(sdf.parse(endDate));
            max.add(calendarType, 1);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Calendar curr = min;
        while (curr.before(max)) {
            result.add(sdf.format(min.getTime()));
            curr.add(calendarType, 1);
        }
        return result;
    }

    /**
     * 根据指定时间获取本月所有日期
     *
     * @param date 2022-01格式
     * @param type now-获取截止当前时间之前的所有日期，all-获取当前月的的所有日期
     * @return 比如传入getAllDateTimeByMonth(" 2022 - 06 ", " now ")，返回[2022-06-01, 2022-06-02, 2022-06-03, 2022-06-04, 2022-06-05, 2022-06-06, 2022-06-07, 2022-06-08]
     * @author mashuai
     */
    @MsExegesis("根据指定时间获取本月所有日期。date@2022-01格式；type@now-获取截止当前时间之前的所有日期，all-获取当前月的的所有日期")
    public static List<String> getAllDayByMonth(ThreadLocal<DateFormat> f, String date, String type) throws ParseException {
        List<String> list = new ArrayList<>();
        Calendar cal = Calendar.getInstance();
        cal.setTime(sdfYm.get().parse(date));
        cal.set(Calendar.DATE, 1);
        int month = cal.get(Calendar.MONTH);
        while (cal.get(Calendar.MONTH) == month) {
            list.add(f.get().format(cal.getTime()));
            cal.add(Calendar.DATE, 1);
        }
        //只取当前时间之前的年月日
        if ("now".equals(type)) {
            return getNowTimeType(list, f.get());
        }
        return list;
    }

    private static List<String> getNowTimeType(List<String> timeList, DateFormat sdf) {
        Date nowDate = new Date();
        Integer nowNums = Integer.valueOf(sdf.format(nowDate).replaceAll("-", ""));
        List<String> collect = timeList.stream().filter(t -> Integer.valueOf(t.replaceAll("-", "")) <= nowNums).collect(Collectors.toList());
        return collect;
    }

    /**
     * 计算两个日期相差多少天，格式取决于Date 格式
     *
     * @param f
     * @param startTime
     * @param endTime
     * @return 负数表示开始时间>结束时间。正数开始时间<结束时间，0表示相等
     * @author mashuai
     */
    @MsExegesis("计算两个日期相差多少天，格式取决于Date 格式。负数表示开始时间>结束时间。正数开始时间<结束时间，0表示相等")
    public static Long calculateDateBetween(ThreadLocal<DateFormat> f, String startTime, String endTime, String type) throws ParseException {
        return calculateDateBetween(f.get().parse(startTime), f.get().parse(endTime), type);
    }

    /***
     * 计算两个日期相差的单位。
     * @param startTime
     * @param endTime
     * @param type day = 天，hour = 小时，minute = 分钟，second=秒
     * @return:
     * @author:mashuai
     */
    @MsExegesis("计算两个日期相差的单位。type@day = 天，hour = 小时，minute = 分钟，second=秒")
    public static Long calculateDateBetween(Date startTime, Date endTime, String type) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(startTime);
        long time1 = cal.getTimeInMillis();
        cal.setTime(endTime);
        long time2 = cal.getTimeInMillis();
        long betweenTime = (time2 - time1);
        if ("day".equals(type)) {
            return betweenTime / (1000 * 60 * 60 * 24);
        }
        if ("hour".equals(type)) {
            return betweenTime / (1000 * 60 * 60);
        }
        if ("minute".equals(type)) {
            return betweenTime / (1000 * 60);
        }
        //默认返回秒
        return betweenTime / (1000);
    }

    /**
     * 判断两个日期是否在  同一年 返回true，在同一年
     */
    @MsExegesis("判断两个日期是否在  同一年 返回true，在同一年")
    public static Boolean isSameYear(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        boolean isSameYear = cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR);
        return isSameYear;
    }

    /**
     * 判断两个日期是否在  同一月
     */
    @MsExegesis("判断两个日期是否在  同一月")
    public static Boolean isSameMonth(Date date1, Date date2) {
        if (!isSameYear(date1, date2)) {
            return false;
        }
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        boolean isSameMonth = cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH);
        return isSameMonth;
    }

    /**
     * 判断两个日期是否在   同一周
     *
     * @return
     */
    @MsExegesis("判断两个日期是否在   同一周")
    public static Boolean isSameWeek(Date date1, Date date2) {
        Calendar cal1 = Calendar.getInstance();
        Calendar cal2 = Calendar.getInstance();
        //西方周日为一周的第一天，咱得将周一设为一周第一天
        cal1.setFirstDayOfWeek(Calendar.MONDAY);
        cal1.setTime(date1);
        cal2.setTime(date2);
        if (cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR))
            return true;
        return false;
    }

    /**
     * 判断两个日期是否在  同一天
     */
    @MsExegesis("判断两个日期是否在  同一天")
    public static Boolean isSameDate(Date date1, Date date2) {
        if (!isSameMonth(date1, date2)) {
            return false;
        }
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        boolean isSameDate = cal1.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH);
        return isSameDate;
    }

    /**
     * 计算两个时间相差的单位 ,计算规则=结束时间-当前时间
     *
     * @param startTime
     * @param endTime
     * @param type      year = 年，month = 月，day = 天，hour = 小时， minute = 分钟 ，second = 秒
     * @param sdf
     * @return long
     * @author mashuai
     */
    @MsExegesis("计算两个时间相差的单位 ,计算规则=结束时间-当前时间。")
    public static float calculativeDiff(String startTime, String endTime, String type, ThreadLocal<DateFormat> sdf) throws ParseException {
        if (null == type || "".equals(type)) {
            type = "second";
        } else {
            long eTime = sdf.get().parse(endTime).getTime();
            long sTime = sdf.get().parse(startTime).getTime();
            float diff = (eTime - sTime);
            if ("second".equals(type)) {
                return diff / 1000;
            } else if ("minute".equals(type)) {
                return diff / (1000 * 60);
            } else if ("hour".equals(type)) {
                return diff / (1000 * 60 * 60);
            } else if ("day".equals(type)) {
                return diff / (1000 * 60 * 60 * 24);
            }
        }
        return 0;
    }

    /**
     * 判断某个时间是否在一个时间段内
     *
     * @param nowTime   当前需要比较时间
     * @param beginTime 开始时间
     * @param endTime   结束时间
     * @return boolean true在
     * @author mashuai
     */
    @MsExegesis("判断某个时间是否在一个时间段内")
    public static boolean timeIsBetweenLimit(Date nowTime, Date beginTime, Date endTime) {
        //设置当前时间
        Calendar date = Calendar.getInstance();
        date.setTime(nowTime);
        //设置开始时间
        Calendar begin = Calendar.getInstance();
        begin.setTime(beginTime);//开始时间
        //设置结束时间
        Calendar end = Calendar.getInstance();
        end.setTime(endTime);//上午结束时间
        //处于开始时间之后，和结束时间之前的判断
        if ((date.after(begin) && date.before(end))) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * @param datetime 需要转换的时间，只支持ymd格式
     * @return
     * @author mashuai
     */
    @MsExegesis("时间转换成星期格式")
    public static String dateToWeek(String datetime) throws ParseException {
        String rq = datetime;
        String[] weekDays = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
        Date date = sdfYmd.get().parse(rq);
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1 < 0 ? 0 : cal.get(Calendar.DAY_OF_WEEK) - 1;
        String week = weekDays[w];
        return week;
    }

    /**
     * 获取指定时间的上个月的月初和月末时间
     *
     * @param time 时间
     * @return 月初@月末
     * @author mashuai
     */
    @MsExegesis("获取指定时间的上个月的月初和月末时间")
    public static String getLastMonthBeginAndEndTime(String time) {
        String timeSplit[] = time.split("-");
        String beginTime = timeSplit[0] + "-" + timeSplit[1] + "-01";
        List<String> timeList = getAllTimeInterval(sdfYmd, beginTime, time);
        //时间往前推n天，推到上个月的月末日期
        String lastMonthEndTime = getBeforeOrAfterTimeDay(sdfYmd, -timeList.size(), time, "day");
        String lastMonthEndTimeSplit[] = lastMonthEndTime.split("-");
        String lastMonthBeginTime = lastMonthEndTimeSplit[0] + "-" + lastMonthEndTimeSplit[1] + "-01";
        return lastMonthBeginTime + "@" + lastMonthEndTime;
    }


    /**
     * 将一段时间按照月或年拆分成指
     *
     * @param sdf   时间格式
     * @param start 开始时间段
     * @param end   结束时间段
     * @param opt   year = 年，month = 月，day = 天，minute = 分钟
     * @param nums  拆分偏移数量
     * @return
     * @author mashuai
     */
    @MsExegesis("将一段时间按照月或年拆分成指")
    public static List<String> getIntervalTimeByType(ThreadLocal<DateFormat> sdf, String start, String end, String opt, int nums) throws Exception {
        List<String> timeList = new ArrayList<>();
        timeList.add(start);
        while (true) {
            String time = getBeforeOrAfterTimeDay(sdf, nums, start, opt);
            start = time;
            float day = calculativeDiff(time, end, "d", sdfYmd);
            System.out.println(day);
            if (day >= 0.0 || "0.0".equals(String.valueOf(day))) {
                timeList.add(time);
            } else {
                return timeList;
            }
        }
    }

    /**
     * 计算相差月
     *
     * @param startDate
     * @param endDate
     * @return
     * @throws ParseException
     */
    @MsExegesis("计算相差月")
    public static long getMonthDiff(String startDate, String endDate) throws ParseException {
        long monthday;
        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
        Date startDate1 = fmt.parse(startDate);
        Calendar starCal = Calendar.getInstance();
        starCal.setTime(startDate1);
        int sYear = starCal.get(Calendar.YEAR);
        int sMonth = starCal.get(Calendar.MONTH);
        int sDay = starCal.get(Calendar.DAY_OF_MONTH);
        Date endDate1 = fmt.parse(endDate);
        Calendar endCal = Calendar.getInstance();
        endCal.setTime(endDate1);
        int eYear = endCal.get(Calendar.YEAR);
        int eMonth = endCal.get(Calendar.MONTH);
        int eDay = endCal.get(Calendar.DAY_OF_MONTH);
        monthday = ((eYear - sYear) * 12 + (eMonth - sMonth));
        return monthday;
    }

}
